package item

import (
	"reflect"
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/group"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/orm"
)

type ItemData struct {
	Items       []SettingItem
	ItemMap     map[int]*SettingItem
	ItemGroup   map[int][]int
	ShopItems   []SettingShopItem
	ShopItemMap map[int]*SettingShopItem

	ItemList     []*Item
	ItemListMap  map[int]*Item
	ShopItemList []*ShopItem

	ItemGroupStat map[int][]string

	// 基本统计
	MaxItemLevel       int
	LastItemUpdateTime *time.Time

	itemModel     *orm.Model
	shopItemModel *orm.Model
}

type language struct {
	Items     map[int]*Item
	ShopItems []*ShopItem
}

func GetItemData(env *game.Environment, extendType reflect.Type, insideType reflect.Type, groupData *GroupData) (*ItemData, error) {
	var err error
	data := &ItemData{}

	if data.itemModel, data.Items, data.ItemMap, err = getItemData(env, extendType, insideType); err != nil {
		return nil, err
	}
	if data.shopItemModel, data.ShopItems, data.ShopItemMap, err = getShopItemData(env); err != nil {
		return nil, err
	}

	itemProvider := &game.DataProvider{
		Array:    true,
		Language: true,
		Name:     "item",
		Get: func() (i interface{}, err error) {
			return data.ItemList, nil
		},
	}
	env.RegisterData(itemProvider)
	shopItemProvider := &game.DataProvider{
		Name:     "shopItem",
		Language: true,
		Array:    true,
		Get: func() (i interface{}, err error) {
			return data.ShopItemList, nil
		},
	}
	env.RegisterData(shopItemProvider)
	// 基本统计
	data.ItemGroup = make(map[int][]int)
	data.ItemGroupStat = make(map[int][]string)
	for k, item := range data.Items {
		if item.Level > data.MaxItemLevel {
			data.MaxItemLevel = item.Level
		}
		if data.LastItemUpdateTime == nil || item.UpdateTime.After(*data.LastItemUpdateTime) {
			data.LastItemUpdateTime = item.UpdateTime
		}
		groupStats := make([]string, 0)
		for _, group := range item.Group {
			g, ok := groupData.GroupMap[group]
			if !ok {
				continue
			}
			if g.StatKey != "" {
				groupStats = append(groupStats, g.StatKey)
			}
			if _, ok := data.ItemGroup[group]; !ok {
				data.ItemGroup[group] = make([]int, 0)
			}
			data.ItemGroup[group] = append(data.ItemGroup[group], item.Id)
		}
		data.ItemGroup[DefaultGroup] = append(data.ItemGroup[DefaultGroup], item.Id)
		data.ItemGroupStat[k] = groupStats
	}

	// item
	data.ItemList = make([]*Item, len(data.Items))
	for key := range data.Items {
		item := NewItem(itemProvider, &data.Items[key])
		data.ItemList[key] = item
		data.ItemListMap[item.Id] = item
	}

	//shopItem
	data.ShopItemList = make([]*ShopItem, 0, len(data.ShopItems))
	for _, shopItem := range data.ShopItems {
		tmp := data.ItemListMap[shopItem.ItemId]
		data.ShopItemList = append(data.ShopItemList, NewShopItem(shopItemProvider, &shopItem, tmp))
	}
	return data, nil
}

func getItemData(env *game.Environment, extendType reflect.Type, insideType reflect.Type) (*orm.Model, []SettingItem, map[int]*SettingItem, error) {
	var err error

	model, err := env.GetModel(SettingItemName)
	if err != nil {
		return nil, nil, nil, err
	}
	var items []SettingItem
	_, err = model.Query().Find(&items)
	if err != nil {
		return nil, nil, nil, err
	}
	itemMap := make(map[int]*SettingItem, len(items))
	for k := range items {
		if extendType != nil {
			items[k].Extend = utils.ToType(items[k].Extend, extendType)
		}
		if insideType != nil {
			items[k].Inside = utils.ToType(items[k].Inside, insideType)
		}
		itemMap[items[k].Id] = &items[k]
	}
	return model, items, itemMap, nil
}

func getShopItemData(env *game.Environment) (*orm.Model, []SettingShopItem, map[int]*SettingShopItem, error) {
	var err error
	model, err := env.GetModel(SettingShopItemName)
	if err != nil {
		return nil, nil, nil, err
	}
	var shopItems []SettingShopItem
	_, err = model.Query().Find(&shopItems)
	if err != nil {
		return nil, nil, nil, err
	}
	shopItemMap := make(map[int]*SettingShopItem, len(shopItems))
	for k := range shopItems {
		shopItemMap[shopItems[k].Id] = &shopItems[k]
	}
	return model, shopItems, shopItemMap, nil
}

func (itemData *ItemData) GetItem() map[int]*Item {
	return itemData.ItemListMap
}

func (itemData *ItemData) GetAllShopItems(area string, updateTime *time.Time) ([]*ShopItem, error) {
	if updateTime != nil {
		items := make([]*ShopItem, 0, 5)
		for _, item := range itemData.ShopItemList {
			if item.HasUpdate(updateTime) {
				items = append(items, item)
			}
		}
		return items, nil
	} else {
		return itemData.ShopItemList, nil
	}
}
